//: Playground - noun: a place where people can play

import UIKit

var str = "Hello, playground"

//:1.常量、变量
let string1:String = "hello"
var string2:String = "world"

let 丿 = 3.14
let a:Int = 1
var b = 6.66

b = 3.141592654
//a = 2   //a是常量
print("b的值是\(b)")

//: 2.类型转换
var g = 6
var h = 0.66
var i = Double(g) + h
let j = Int(i)

//: 3.元组
let pp = (n:404,"Not Found",3.14)//可以给单个元素命名
let (c,d,e) = pp
print("c:\(c)  d:\(d) e:\(e)")

let (_,_, f) = pp
print("\(f) ----- \(pp.1)")

//: 4.可选类型
var errorCode: Int? = 404
//errorCode = nil
//print(errorCode)
var errorMessage: String?

if errorCode != nil {                  // 确定可选类型包含值后可以在变量名后加!强制解析
    print(errorCode!)
}
//print(errorMessage!)
//:只声明可选类型没赋值,会默认设置为nil  swift中nil的含义:nil是一个值,表示常量或变量的缺失,所以任何类型都可以设置为nil
//:使用if语句强制解析,可选类型不能直接使用需要转化
if let code = errorCode {
    print(code)                        // 输出:404, 自动解析
}else {
    //    print(code)                      // ❌,code是局部变量
//    print(errorCode)                   // 如果errorCode = nil此处输出nil
}


//: 5基本运算符
-11 % 3
11 % -3

let optionalE: Int? = 4
let k = 10
let l = optionalE ?? k
//:  ?? 前面那个值要为可选类型   如果optionalE为空,l为k的值  如果不为空就是optionalE解包后的值
1...5     // 定义1到5,5个数,包含1和5
1..<4     // 定义1到5,4个数,不包含5

//:  6字符串操作
var emptyString1 = ""                  // 空字符串,""内为空
let emptyString2 = String()            // 空字符串,通过String()初始化方法置为空
emptyString1.isEmpty
emptyString1 = "sadalsja"
//emptyString2 = "sdsfds"

var char: Character?
for char in emptyString1.characters {
    print(char)                // 依次输出emptyString1中所有字符
}
emptyString1.characters.count


var string4 = "Hello"
var string5 = " World"
var string3 = string4 + string5        // Hello World, 通过"+"将两个字符串相连
string4 += string5
string3.append(" !")                   // Hello World !, 通过append()方法拼接
let string6 = "Hello\(string5) !"

let messChar = string6[string6.startIndex]  // H,通过第一个字符的索引获取字符
let firstIndex = string6.index(after: string6.startIndex)
let endIndex = string6.index(before: string6.endIndex)
let totalCount = string6.characters.count

let index = string6.index(string6.startIndex, offsetBy: 4)  // 4, 初始位置偏移4
string6[index]
string6[endIndex]

var welcome = "Hello"
welcome.insert("!", at: welcome.endIndex)
welcome.insert(contentsOf: " World".characters, at: welcome.index(before: welcome.endIndex))

welcome.remove(at: welcome.index(before: welcome.endIndex))
print(welcome)

let range = welcome.index(welcome.endIndex, offsetBy: -6) ..< welcome.endIndex
welcome.removeSubrange(range)

welcome.hasPrefix("He")
welcome.hasSuffix("llo")

//: 7数组
var arrInts = [Int]()                 // [], 定义一个元素类型为Int的可变空数组

arrInts.append(100)                      // [2], arrInts中添加一个为2的元素

arrInts = []                           // [], 空数组的简单定义方式

arrInts = [1,2,3]                     // [1, 4], 数组的简单构造方法

var threeDouble = [Double](repeating: 0.0, count: 3)
var twoDouble = [Double](repeating: 2.0, count: 2)

var fiveDouble = threeDouble + twoDouble  // 数组也能用"+"拼接

twoDouble += threeDouble
print(twoDouble)                        // [2.0, 2.0, 0.0, 0.0, 0.0]

twoDouble[1...2] = [3.0, 3.0]          // 将数组中的[1...2]区间改为[3.0, 3.0]中的元素
print(twoDouble)

twoDouble.insert(4.0, at: 0)           // 将4.0插入到数组第一个元素
twoDouble.remove(at: 0)                // 移除0位置的元素,之前后面的元素会自动往前一位
twoDouble.removeLast()                 // 移除最后一个元素

for (index, value) in twoDouble.enumerated() {  // 使用迭代器获取数组元素以及索引号
    print("索引:\(index), 元素:\(value)")  // 依次打印索引号及元素 索引号:0, 元素:3.0 ...
}

//:  8集合            集合：存储相同类型没有确定顺序的元素,相同元素只能出现一次
var char1 = Set< Character >()          // 创建一个Character类型的空集合

let char2:Set< Character > = []         // 定义空集合的简便方法.类型:Set< Character >

var letter1: Set = ["a","b","c"]
var letter2 = ["a","b","c"]   // Set省略后,根据自动类型推断,letter2就是数组类型

letter1.count
letter1.isEmpty

letter1.insert("a")                    // 如果插入的元素如果已经在集合中,那么会被忽略
print(letter1)                         // ["b", "a", "c"], 元素没有顺序

letter1.remove("c")                    // c, 要删除的元素在集合中,则返回元素,否则返回nil
print(letter1)                         // ["b", "a"]

letter1.contains("a")                  // true, 是否包含某个元素

for char in letter1 {
    print(char)                      // 不按顺序的输出集合中每个元素
}

for char in letter1.sorted() {
    print(char)                      // 按特定顺序的输出集合中每个元素
}


var num1: Set = ["3", "2", "1", "5", "7"]
let num2: Set = ["3", "2", "1", "5", "7"]
let num3: Set = ["1", "3", "5"]

num1.intersection(num2)              // ["1", "3", "5"], 两个集合都包含的值的集合

num1.union(num2)                     // 两个集合的所有元素组成的集合

num1.subtracting(num2)               // ["2", "7"], 只在num1中有的元素组成的集合

num1.symmetricDifference(num2)       // 在一个集合中但不在两个集合中的元素组成的集合


num2 == num3                         // false, 判断两个集合包含的元素是否都相同

num3.isSubset(of: num2)              // true, num3是num2的子集

num2.isSuperset(of: num3)            // true, num2是num3的超集合

num1.isDisjoint(with: num3)          // false, 判断两个集合中的元素是否都不相同


//:  9字典
var chars : [String:String] = ["char1":"A","char2":"B"]
var charDict = ["char1":"A", "char2":"B"]  // 系统会作自动类型推断,类型也可以省略
chars.count                                // 2

chars.isEmpty                              // false

chars["char3"] = "C"                       // chars中新增一个键值对

chars["char1"] = "a"                       // chars中修改key对应的value

chars.updateValue("b", forKey: "char2")   // 更新键值对,要是这个键不存在则会添加一个

print(chars)                               // ["char3": "C", "char2": "B", "char1": "a"]

chars["char3"] = nil                       // 将key对应的value设置为nil,移除键值对

chars.removeValue(forKey: "char2")         // 移除键值对

print(chars)                               // ["char1": "a"]


for (key, value) in charDict {             // 遍历字典中的所有key value
    print("\(key): \(value)")              // char2: B 换行 char1: A
}
for key in charDict.keys {                 // 遍历字典中的所有key
    print(key)                             // char2 换行 char1
}

for value in charDict.values {             // 遍历字典中的所有value
    print(value)                           // B 换行 A
}

let arrKeys = [String](charDict.keys)      // ["char1", "char2"], 获取字典中所有key的数组

let arrValues = [String](charDict.values)  // ["A", "B"], 获取字典中所有value的数组

//:  11控制流
for num in 1...5{
    print(num)
}

for _ in 1...5{
    print("A")
}

var number = 3
//while number>0 {
//    print(number)
//    number = number - 1
//}

var num123 = 12
switch num123 {
case 2:
    print("num等于2")                  // 每个case分支至少需要包含一条语句
    print("num等于2")                  // case分支末尾不需要写break,不会发生贯穿
case 3, 4, 5:                         // case语句可以匹配多个值,之间用(,)隔开
    print("num == 3 or 4 or 5")
case 6..<10:                          // case语句也支持区间
    print("num大于等于6,且小于10")
case 10..<19 where num123 % 3 == 0:      // 使用where语句来增加额外判断条件
    print("num大于等于10,且小于19,且能被3整除")
    fallthrough                // 贯穿,使用fallthrough可以连续到下一个case中
default:
    print("上面的情况都不满足")
}

//: 12函数
func greet(person: String) -> String {  // func 函数名(参数名: 参数类型) -> 返回值类型
    // 函数内的代码块,此处省略
    return "Hello, \(person)!"          // return 返回值
}
let greetWord = greet(person: "Dariel") // 函数名(参数名: 参数)
print(greetWord)                        // Hello, Dariel! 打印返回值



func say() -> String{                   // 函数无参数有返回值
    return "Hello"
}
print(say())                            // Hello


func say(person1: String, person2: String) -> String {  // 多个参数且有返回值
    return "Hello,\(person1) \(person2)"
}
print(say(person1: "Lily", person2: "Lucy"))  // Hello,Lily Lucy



func sayHello() {                       // 无参数无返回值
    print("Hello")                      // Hello
}
sayHello()


//: 求数组中的最大值和最小值
func minMax(array: [Int]) -> (min: Int, max: Int)? { // 使用元组返回多个参数
    if array.isEmpty {return nil}
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1 ..< array.count] {
        if value < currentMin {
            currentMin = value
        }else if value > currentMax{
            currentMax = value
        }
    }
    return (currentMin, currentMax)     // 考虑到数组为空的情况,返回值为可选类型
}

let bounds = minMax(array: [3,5,6,12,8,10]) // bounds是一个元组类型
print(bounds!.max)                      // 通过点语法获取最值
print(bounds!.min)                      // !强制解包,表示一定有值




func someFunction(label name: String) -> String{ // label参数标签, name 参数名称
    return name                         // 在函数实现中用参数名称
}
someFunction(label: "Dariel")           // 在调用函数的时候用参数标签
// 参数标签的使用目的: 使函数在调用的时候的表达更有力,并且能够保持函数内部的可读性



func ignoreLabel(_ name1: String, label2 name2: String) {  // 下划线(_)来代替标签
    print(name1 + name2)                // LilyLucy
}
ignoreLabel("Lily", label2: "Lucy")     // 忽略的参数标签此处不会显示


func defaultLabel(label1 name1: String, label2 name2: String = "Lily") {
    print(name1 + name2)
}
defaultLabel(label1: "Lucy", label2: "Lilei")  // LucyLilei
defaultLabel(label1: "Lucy")            // LucyLily, 有默认参数可以省略


//:  计算多个数的平均数
func avarageNum(_ numb: Double...) -> Double{
    var total: Double = 0
    for number in numb{
        total += number
    }
    return total/Double(numb.count)
}
avarageNum(2,5,7,8,9)
avarageNum(10)               // 注意: 一个函数只能有一个可变参数
33


func swapTwoInts(_ m: inout Int,_ n: inout Int) {  //  加inout可以传指针
    let temp = m
    m = n
    n = temp
}
var m = 2
var n = 3
swapTwoInts(&m, &n)                     // 此处传进去的是m,n两个变量的地址
print("m=\(m), n=\(n)")                 // m=3, n=2, swapTwoInts后m,n的值改变了


var swap = swapTwoInts                  // swap的类型为(inout Int, inout Int) -> ()
swap(&m, &n)
print("m=\(m), n=\(n)")                 // a=2, b=3, 函数swap等价于swapTwoInts
// 可以把一个函数当作常量或变量赋值给一个常量或者变量



//:  函数作为参数传入另一个函数
func sum(num1: Int, num2: Int) -> Int { // 和
    return num1 + num2
}
func sub(num1: Int, num2: Int) -> Int { // 差
    return num1 - num2
}

func mathNumber(_ mathFunc: (Int, Int) -> Int, _ a: Int, _ b: Int) -> Int{
    return (mathFunc(a, b))
}

mathNumber(sum, 4, 2)                   // 6, 将函数作为参数传进去
mathNumber(sub, 4, 2)                   // 2


//:  函数的返回值是另外一个函数
func chooseSumOrSub(_ isSum : Bool) -> (Int,Int) -> Int{
    return isSum ? sum : sub
}
chooseSumOrSub(true)(4,2)

//: 函数的嵌套
func chooseSumOrSub1(isSum:Bool , _ a: Int,_ b: Int) -> Int{
    func sum(num1: Int, num2: Int) -> Int {return num1 + num2}
    func sub(num1: Int, num2: Int) -> Int {return num1 - num2}
    
    return isSum ? sum(num1: a, num2: b) : sub(num1: a, num2: b)
}
chooseSumOrSub1(isSum: false, 6, 4)









//:   函数的返回值是另外一个函数